import base64
import pickle
from django.shortcuts import render
from django_redis import get_redis_connection
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from cart import constants
from cart.serializers import CartSerializer, CartGetSerializer, CartDeleteSerializer, CartSelectSerializer
from goods.models import SKU

# Create your views here.
# 购物车记录接口
class CartView(APIView):
    def perform_authentication(self, request):
        """重写这个方法为了让非登录用户可以访问这个接口"""
        pass

    # POST: /cart/
    def post(self,request):
        """添加商品到购物车"""
        # 1 获取参数并校验(sku_id必传,selected,count必传,校验sku_id对应的商品是否存在)
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 2 将购物车数据进行存储
        # 组织数据
        sku_id = serializer.validated_data['sku_id']
        count = serializer.validated_data['count']
        selected = serializer.validated_data['selected']
        try:# 如果传的错误的JWTtoken 会request.user会报错为None and会先判断左侧左侧为None直接else
            # 如果不传JWTtoken 就是匿名用户对象.is_authenticated会是False
            user = request.user
        except Exception: # 让非登录用户可以访问这个接口
            user = None
        #2.1登录用存储在redis
        if user and user.is_authenticated:
            redis_conn = get_redis_connection('cart')

            cart_userid = 'cart_%s' % user.id
            # 在redis中存储hash数据:商品id和数量
            redis_conn.hincrby(cart_userid,sku_id,count)# field存在则累加

            # 在redis中存储set数据:选中的加入set
            cart_selected_userid = 'cart_selected_%s'%user.id
            if selected:
                redis_conn.sadd(cart_selected_userid,sku_id)

            # 将数据序列化返回:保存成功
            return Response(serializer.data,status=status.HTTP_201_CREATED)

        #2.2未登录用户存储在cookie
        else:
            cookie_cart = request.COOKIES.get('cart') # 可能为None

            if cookie_cart:
                # 解析cookie中购物车数据
                # {
                #     '<sku_id>': {
                #         'count': '<count>',
                #         'selected': '<selected>'
                #     },
                #     ...
                # }
                cart_dict = pickle.loads(base64.b64decode(cookie_cart))
            else:
                cart_dict = {}

            # 如果购物车已经添加过该商品,数量需要进行累加
            if sku_id in cart_dict:
                count += cart_dict[sku_id]['count']
            cart_dict[sku_id] = {
                'count':count,
                'selected':selected
            }

            # 定义响应对象
            response = Response(serializer.data,status=status.HTTP_201_CREATED)
            cart_data = base64.b64encode(pickle.dumps(cart_dict)).decode()

            # 给响应设置cookie
            response.set_cookie('cart',cart_data,max_age=constants.CART_COOKIE_EXPIRES)
            return response

    # GET /cart/
    def get(self,request):
        """购物车记录获取接口"""
        # 1 获取用户的购物车记录
        try:
            user = request.user
        except Exception:
            user = None
        # １.1如果是登录用户从redis中取
        if user and user.is_authenticated:
            redis_conn = get_redis_connection('cart')

            cart_userid = 'cart_%s' % user.id
            # 获取reids-hash中所有的属性和值
            # {
            #     b'<sku_id>': b'<count>',
            #     ...
            # }
            cart_redis = redis_conn.hgetall(cart_userid)
            # 从redis set中获取用户勾选的商品id
            cart_selected_userid = 'cart_selected_%s' % user.id
            # (b'<sku_id>', ...)
            cart_selected_redis = redis_conn.smembers(cart_selected_userid)
            # { # 就是要组织成当初存储在cookie这种形式
            #     '<sku_id>': {
            #         'count': '<count>',
            #         'selected': '<selected>'
            #     },
            #     ...
            # }
            cart_dict = {}
            for sku_id,count in cart_redis.items():
                cart_dict[int(sku_id)] = {
                    'count':int(count),
                    'selected':sku_id in cart_selected_redis
                    # 三元运算True if sku_id in cart_selected_redis else False
                }

        #1.2非登录用户从cookie中取
        else:
            # 获取cookie中的购物车数据
            cookie_cart = request.COOKIES.get('cart') # 可能为None
            if cookie_cart:
                # 解析cookie中购物车数据
                # {
                # '<sku_id>': {
                # 'count': '<count>',
                # 'selected': '<selected>'
                # },
                # ...
                # }
                cart_dict = pickle.loads(base64.b64decode(cookie_cart))
            else:
                cart_dict={}

        #２ 根据购物车记录里面的id返回商品的数据
        sku_ids = cart_dict.keys()
        skus = SKU.objects.filter(id__in=sku_ids)

        for sku in skus:
            # 给sku对象增加属性count和selected，分别保存该商品在购物车中添加数量和勾选状态
            sku.count = cart_dict[sku.id]['count']
            sku.selected = cart_dict[sku.id]['selected']

        # 3 将数据序列化并返回
        serializer = CartGetSerializer(skus,many=True)
        return Response(serializer.data)
    # PUT /cart/
    def put(self,request):
        """修改购物车商品记录"""
        # 1 获取参数进行校验(sku_id（商品存在否）,改后count（库存足够否）,改后selected)
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 组织校验后的参数
        sku_id = serializer.validated_data['sku_id']
        count = serializer.validated_data['count']
        selected = serializer.validated_data['selected']
        # 2 修改用户购物车数据
        try:
            user = request.user
        except Exception:
            user = None
        #２.1登录用户修改redis中的hash&set
        if user and user.is_authenticated:
            redis_conn = get_redis_connection('cart')

            cart_userid = 'cart_%s' % user.id
            redis_conn.hset(cart_userid,sku_id,count)

            cart_selected_userid = 'cart_selected_%s' % user.id
            if selected:
                # 集合中元素是唯一的
                redis_conn.sadd(cart_selected_userid, sku_id)
            else:
                # 有则移除,无不报错
                redis_conn.srem(cart_selected_userid,sku_id)
            # 返回响应
            return Response(serializer.data)
        #2.2 非登录用户修改cookie中的数据
        else:
            response = Response(serializer.validated_data)
            # 获取cookie中的数据
            cart_cookie = request.COOKIES.get('cart')
            if cart_cookie is None:
                return response
            # 解析cookie中购物车数据
            # {
            #     '<sku_id>': {
            #         'count': '<count>',
            #         'selected': '<selected>'
            #     },
            #     ...
            # }
            cart_dict = pickle.loads(base64.b64decode(cart_cookie))
            if not cart_dict:
                return response
            # 字典中添加数据
            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }

            # 编码成字符串设置cookie
            cart_data = base64.b64encode(pickle.dumps(cart_dict)).decode()
            response.set_cookie('cart',cart_data,max_age=constants.CART_COOKIE_EXPIRES)

            return response
    # DELETE /cart/
    def delete(self,request):
        """购物车数据删除"""
        # 1 获取参数校验(sku_id必传,sku_id这个商品是否存在)
        serializer = CartDeleteSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        sku_id = serializer.validated_data['sku_id']

        # 2 删除对应的购物车数据
        try:
            user = request.user
        except Exception:
            user = None
        # 2.1 用户登录,删除对应redis数据
        if user and user.is_authenticated:
            redis_conn = get_redis_connection('cart')
            cart_userid = 'cart_%s' % user.id
            redis_conn.hdel(cart_userid,sku_id)

            cart_selected_userid = 'cart_selected_%s' % user.id
            redis_conn.srem(cart_selected_userid,sku_id) # 不存在不会报错,所以不用判断

            return Response(status=status.HTTP_204_NO_CONTENT)
        # 2.2 用户未登录,删除cookie中数据
        else:
            response = Response(status=status.HTTP_204_NO_CONTENT)
            # 获取cookie数据
            cart_cookie = request.COOKIES.get('cart')
            if cart_cookie is None:
                return response
            # 解析数据
            cart_dict = pickle.loads(base64.b64decode(cart_cookie))

            # 如果商品id在cookie中存在则删除
            if sku_id in cart_dict:
                del cart_dict[sku_id] # 不存在会报错,需要判断
            # 重新设置cookie
            cart_data = base64.b64encode(pickle.dumps(cart_dict)).decode()
            response.set_cookie('cart',cart_data,max_age=constants.CART_COOKIE_EXPIRES)
            return response

# 购物车全选和取消全选
# PUT /cart/selection/
class CartSelectedAPIView(APIView):
    def perform_authentication(self, request):
        """重写这个方法为了让非登录用户可以访问这个接口"""
        pass

    def put(self,request):
        # 1. 获取参数selected并进行校验(selected必传)
        serializer = CartSelectSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 获取校验之后的selected
        selected = serializer.validated_data['selected'] # True: 全选 False: 全不选

        try:
            # request.user会触发DRF框架认证过程
            user = request.user
        except Exception:
            user = None

        # 2. 设置用户购物车记录勾选状态
        if user and user.is_authenticated:
            # 2.1 如果用户已登录，设置redis中用户购物车记录勾选状态
            # 获取redis链接
            redis_conn = get_redis_connection('cart')

            # 从redis hash中获取用户购物车中所有商品的id
            cart_userid = 'cart_%s' % user.id
            # [b'<sku_id>', b'<sku_id>', ...]
            cart_keys_list = redis_conn.hkeys(cart_userid)
            cart_selected_userid = 'cart_selected_%s' % user.id
            if selected:
                redis_conn.sadd(cart_selected_userid,*cart_keys_list)
            else:
                redis_conn.srem(cart_selected_userid,*cart_keys_list)
            return Response({'message':'设置成功'})
        else:
            # 2.2 用户未登录,设置cookie中的数据勾选状态
            response = Response({'message':'设置成功'})
            cart_cookie = request.COOKIES.get('cart')
            if cart_cookie:

                # 解析数据
                cart_dict = pickle.loads(base64.b64decode(cart_cookie))
            else:
                cart_dict = {}

            for sku_id, dict in cart_dict.items():
                cart_dict[sku_id]['selected'] = selected # 这里的selected传过来就是bool
            # 编码设置cookie
            cart_data = base64.b64encode(pickle.dumps(cart_dict)).decode()
            response.set_cookie('cart', cart_data,max_age=constants.CART_COOKIE_EXPIRES)

            return response


